package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.admin.NewsAutoScanConstants;
import com.heima.common.constants.message.WmNewsMessageConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.NewsAuthDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.*;
import com.heima.model.wemedia.vo.WmNewsVo;
import com.heima.utils.threadlocal.WmThreadLocalUtils;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Transactional
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Value("${file.oss.web-site}")
    private String webSite; //素材的OSS域名

    @Override
    public ResponseResult list(WmNewsPageReqDto dto) {
        //1.判断请求参数
        if(dto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.判断分页参数设置默认值
        dto.checkParam();

        //3.判断用户是否登录
        WmUser wmUser = WmThreadLocalUtils.getUser();
        if(wmUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //4.判断每个参数动态拼接查询条件
        if(dto.getStatus()!=null){
            lambdaQueryWrapper.eq(WmNews::getStatus, dto.getStatus()); //动态拼接根据状态查询
        }

        if(StringUtils.isNotBlank(dto.getKeyword())){
            lambdaQueryWrapper.like(WmNews::getTitle, dto.getKeyword()); //动态拼接根据标题模糊查询
        }

        if(dto.getChannelId()!=null){
            lambdaQueryWrapper.eq(WmNews::getChannelId, dto.getChannelId()); //动态拼接根据频道ID查询
        }

        if(dto.getEndPubDate()!=null &&  dto.getBeginPubDate()!=null){
            //动态拼接根据发布时间范围查询
            lambdaQueryWrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        }

        //5.设置固定查询条件：当前登录用户ID，按照创建时间倒排
        lambdaQueryWrapper.eq(WmNews::getUserId, wmUser.getId());
        lambdaQueryWrapper.orderByDesc(WmNews::getCreatedTime);

        //6.执行分页查询
        IPage<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        this.page(page,lambdaQueryWrapper);

        //7.封装分页响应结果数据，注意添加上域名
        ResponseResult responseResult  = new PageResponseResult(dto.getPage(),dto.getSize(),(int)page.getTotal());
        responseResult.setData(page.getRecords());
        responseResult.setHost(webSite);

        //8.响应数据
        return responseResult;
    }

    @Autowired
    private KafkaTemplate kafkaTemplate;


    @Override
    public ResponseResult submit(WmNewsDto dto, Short isSubmit) {
        //第一部分：准备校验
        //1.1.判断参数是否合法
        if(dto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //1.2.判断用户是否登录
        WmUser wmUser = WmThreadLocalUtils.getUser();
        if(wmUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //第二部分：文章创建或修改
        //2.1 复制dto给wmnews
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto,wmNews);

        //2.2 判断布局方式如果是自动布局，设置布局方式暂时为空
        if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
            wmNews.setType(null);
        }

        //2.3 将封面图片列表数据转为逗号分割的字符串数据
        //前端传过来的图片地址是列表数据，格式如：[ "https://hmleadnews.oss-cn-beijing.aliyuncs.com/material/2021/5/20210520/3ac77c9e4dcf4bbf91e34c5fa6465894.png","https://hmleadnews375.oss-cn-beijing.aliyuncs.com/material/2021/5/20210520/5227811087e8451f80282f7b12fa87c3.png"]
        //我们需要将列表数据转换为逗号分割的字符串数据，最终格式如："/material/2021/5/20210520/3ac77c9e4dcf4bbf91e34c5fa6465894.png","/material/2021/5/20210520/5227811087e8451f80282f7b12fa87c3.png"
        List<String> coverImageList = dto.getImages();
        if(coverImageList!=null && coverImageList.size()>0){
            //遍历列表去掉所有图片地址的域名前缀
            coverImageList = coverImageList.stream().map(x -> x.replace(webSite, "").replace(" ", "")).collect(Collectors.toList());

            //将列表转为逗号分割的字符串
            String coverImages = StringUtils.join(coverImageList, ",");
            wmNews.setImages(coverImages);
        }


        //2.4 执行文章创建或修改
        saveWmNews(wmNews, isSubmit);


        //抽取正文中图片的地址列表
        List<String> contentImageList = extractContent(dto.getContent());
        //第三部分：保存内容中图片与文章的关系（当前提交审核及正文中有图片时才执行）
        if(isSubmit.equals(WmNews.Status.SUBMIT.getCode()) && contentImageList.size()>0){
            ResponseResult responseResult = saveRelationForContent(contentImageList, wmNews.getId());
            if(responseResult!=null){
                return responseResult;
            }
        }

        //第四部分：保存封面中图片与文章的关系（当前提交审核时才执行）
        if(isSubmit.equals(WmNews.Status.SUBMIT.getCode())){
           ResponseResult responseResult = saveRelationForCover(contentImageList, wmNews, dto);
            if (responseResult != null) {
                return responseResult;
            }

            //文章提交审核后，需要将文章ID发送到MQ
            kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, String.valueOf(wmNews.getId()));
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    //保存封面图片与文章的关系
    private ResponseResult saveRelationForCover(List<String> contentImageList, WmNews wmNews, WmNewsDto dto) {
        //1.获取封面图片地址列表（如果列表有值说明页面选择的是非自动布局，这个时候跟正文图片没有关系，反之就从正文图片中取）
        List<String> coverImageList = dto.getImages();


        //如果页面选用的是自动布局，则封面图片从正文中取（按照规则匹配）
        if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
            int size = contentImageList.size();
            if(size>=3){ //匹配规则1：如果正文图片数量大于3，则取3张当做封面图片
                coverImageList= contentImageList.stream().limit(3).collect(Collectors.toList());
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE); //多图布局
            } else if(size>=1 && size <3){//匹配规则2：如果正文图片数量大于等于1小于3，则取1张当做封面图片
                coverImageList= contentImageList.stream().limit(1).collect(Collectors.toList());
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE); //单图布局
            } else {//匹配规则2：如果正文图片没有，则设置无图布局
                coverImageList = new ArrayList<>();
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE); //无图布局
            }

            if(coverImageList.size()>0){
                coverImageList = coverImageList.stream().map(x->x.replace(webSite,"").replace(" ","")).collect(Collectors.toList());
                String coverImages = StringUtils.join(coverImageList,",");
                wmNews.setImages(coverImages);
                updateById(wmNews);// 更新文章
            }
        }

        //2.如果地址有值，就保存关系
        if(coverImageList!=null && coverImageList.size()>0){
            coverImageList = coverImageList.stream().map(x->x.replace(webSite,"").replace(" ","")).collect(Collectors.toList());
            saveRelation(coverImageList, wmNews.getId(), WemediaConstants.WM_COVER_REFERENCE);
        }
        return null;
    }

    //保存内容图片与文章的关系
    private ResponseResult saveRelationForContent(List<String> contentImageList, Integer newsId) {
        ResponseResult responseResult = saveRelation(contentImageList, newsId, WemediaConstants.WM_CONTENT_REFERENCE);
        if(responseResult!=null){
            return responseResult;
        }
        return null;
    }


    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    /**
     * 公共方法：用来保存素材与文章的关联关系
     * @param imageList   素材地址列表
     * @param newsId  自媒体文章ID
     * @param type  引用关系类型：0-正文引用  1-封面引用
     * @return
     */
    private ResponseResult saveRelation(List<String> imageList, Integer newsId, Short type) {
        //1.根据图片地址列表通过IN查询获取到素材对象列表
        List<WmMaterial> wmMaterialList = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, imageList));

        //2.处理没有查到的素材（举个例子：如果地址有3个，结果只查到2个素材对象，说明有一个地址没查到就要报错）

        //将查出来的素材对象列表转为MAP，其中MAP的key是URL，map的value是素材的id
        Map<String, Integer> wmMaterialMap = wmMaterialList.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));

        //素材ID列表
        List<Integer> materialIdList = new ArrayList<>();
        for (String url : imageList) {
            Integer materialId = wmMaterialMap.get(url);
            if(materialId==null){ //这里就找到了没有查到素材对象的那个素材URL地址，响应错误状态码即可
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "素材不存在");
            }
            //3.添加所有查到的素材的ID
            materialIdList.add(materialId);
        }

        //4.调用mapper保存素材与文章的关系
        wmNewsMaterialMapper.saveRelationsByContent(materialIdList,newsId,type);
        return null;
    }

    //从内容中抽取内容图片地址列表（去除前缀）
    private List<String> extractContent(String content) {
        List<String> contentImageList = new ArrayList<>();
        if(StringUtils.isNotBlank(content)){
            List<Map> mapList = JSON.parseArray(content, Map.class);
            if(mapList!=null && mapList.size()>0){
                for (Map<String,String> map : mapList) {
                    if(map.get("type").equals("image")){
                        String imageUrl = map.get("value");
                        if(imageUrl.startsWith(webSite)){
                            imageUrl = imageUrl.replace(webSite,"").replace(" ", "");
                        }
                        contentImageList.add(imageUrl);
                    }
                }
            }
        }
        return contentImageList;
    }

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    //创建或修改文章
    private void saveWmNews(WmNews wmNews,Short isSumit) {
        wmNews.setUserId(WmThreadLocalUtils.getUser().getId()); //自媒体用户ID
        wmNews.setCreatedTime(new Date());//创建时间
        wmNews.setSubmitedTime(new Date());//提交时间
        wmNews.setStatus(isSumit); //0-保存草稿  1-提交审核
        wmNews.setEnable((short)1);//0-未上架， 1-已上架  默认已上架状态

        //如果ID存在则修改文章，同时删除文章与素材的引用关系
        if(wmNews.getId()!=null && wmNews.getId()>0){

            //删除引用关系
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));

            //更新文章
            updateById(wmNews);

        } else { //如果ID不存在，就保存文章
            save(wmNews);
        }

    }


    @Override
    public ResponseResult findOne(Integer id) {
        //1.判断参数合法性
        if(id==null || id==0){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.判断用户是否登录
        WmUser wmUser = WmThreadLocalUtils.getUser();
        if(wmUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //3.查询文章判断是否存在
        WmNews wmNews = getById(id);
        if(wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //4.设置HOST并响应
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        responseResult.setHost(webSite);

        return responseResult;
    }


    @Override
    public ResponseResult delOne(Integer id) {
        //1.判断参数合法性
        if(id==null || id==0){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.判断用户是否登录
        WmUser wmUser = WmThreadLocalUtils.getUser();
        if(wmUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //3.查询文章判断是否存在
        WmNews wmNews = getById(id);
        if(wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //4.判断如果是已上架已发布，则不能删除
        if(wmNews.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE_UP) &&  wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIC_ERROR, "文章不能删除");
        }

        //5.删除文章
        removeById(id);

        //6.删除引用关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, id));

        //7.响应数据
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }



    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        //1.判断参数合法性
        if(dto==null || dto.getId()==null || dto.getId()==0 ){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.判断用户是否登录
        WmUser wmUser = WmThreadLocalUtils.getUser();
        if(wmUser==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //3.查询文章判断是否存在
        WmNews wmNews = getById(dto.getId());
        if(wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //4.判断文章如果不是发布状态不能上下架
        if(!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIC_ERROR, "必须是发布状态才能上下架");
        }

        //5.执行修改上下架状态
        WmNews wmNewsDB = new WmNews();
        wmNewsDB.setId(dto.getId());
        wmNewsDB.setEnable(dto.getEnable());
        updateById(wmNewsDB);


        //生产文章状态数据到kafka
        Map map = new HashMap();
        map.put("articleId", wmNews.getArticleId()); //APP文章iD
        map.put("enable", dto.getEnable()); //自媒体文章上下架状态

        kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(map));

        //6.响应结果
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    @Override
    public List<Integer> findRelease() {
        /**
         * 查询条件：状态是 4或8  且  发布时间小于系统当前时间
         * select id from wm_news
         */
        List<WmNews> wmNewsList = list(Wrappers.<WmNews>lambdaQuery()
                .in(WmNews::getStatus, WmNews.Status.ADMIN_SUCCESS.getCode(), WmNews.Status.SUCCESS.getCode())
                .le(WmNews::getPublishTime, new Date())
                .select(WmNews::getId)
        );

        List<Integer> idList = new ArrayList<>();
        if(wmNewsList!=null && wmNewsList.size()>0){
            idList = wmNewsList.stream().map(WmNews::getId).collect(Collectors.toList());
        }
        return idList;
    }
    
    
    @Autowired
    private WmNewsMapper wmNewsMapper;


    @Override
    public ResponseResult listVo(NewsAuthDto dto) {
        //1.判断参数
        if(dto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.检查分页参数设置默认值
        dto.checkParam();

        Integer pageNo = dto.getPage();//页码
        //将页码转换为数据开始查询的位置（第几条）
        dto.setPage((pageNo-1)*dto.getSize());

        //3.调用mapper查询数据列表
        List<WmNewsVo> wmNewsVoList = wmNewsMapper.findListAndPage(dto);

        //4.调用mapper查询总记录数
        int total = wmNewsMapper.findListCount(dto);

        //5.封装分页响应结果并设置host域名
        ResponseResult responseResult = new PageResponseResult(pageNo,dto.getSize(),total);
        responseResult.setData(wmNewsVoList);
        responseResult.setHost(webSite);

        //6.响应结果
        return responseResult;
    }

    @Autowired
    private WmUserMapper wmUserMapper;


    @Override
    public ResponseResult oneVo(Integer id) {
        //1.判断参数
        if(id==null || id==0){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.根据ID查询判断是否存在
        WmNews wmNews = getById(id);
        if(wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        //3.根据wm_user_id查询wm_user
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        String authorName = wmUser.getName(); //自媒体用户名就是作者名

        //4.将wmNews复制给wmNewsVo
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews,wmNewsVo);
        wmNewsVo.setAuthorName(authorName);

        //5.封装响应结果数据设置host
        ResponseResult responseResult = ResponseResult.okResult(wmNewsVo);
        responseResult.setHost(webSite);

        //6.响应结果
        return responseResult;
    }


    @Override
    public ResponseResult auth(NewsAuthDto dto, Short status) {
        //1.判断参数
        if(dto==null ||dto.getId()==null || dto.getId()==0){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.根据ID查询判断是否存在
        WmNews wmNews = getById(dto.getId());
        if(wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        //3.判断文章状态必须是待人工审核
        if(!wmNews.getStatus().equals(WmNews.Status.ADMIN_AUTH.getCode())){
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIC_ERROR, "文章状态不对，不能审核");
        }

        //4.根据ID修改文章状态及原因
        WmNews wmNewsDB = new WmNews();
        wmNewsDB.setId(dto.getId());
        wmNewsDB.setStatus(status);
        if(StringUtils.isNotBlank(dto.getMsg())){
            wmNewsDB.setReason(dto.getMsg());
        }
        updateById(wmNewsDB);

        //TODO 5.如果审核通过，应该创建APP文章及修改自媒体文章状态为已发布（9）

        //6.响应结果
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}
